
#include "../../include/common.h"
#include "../../include/core/coroutine.h"
#include "../../include/core/sched.h"

extern coroutine_t* coroutines[TASK_SIZE];

coroutine_t* current_coroutine = NULL;

static coroutine_t* find_ready_minithread() {
    coroutine_t* ready_coroutine;

    for (int i = 0; i < TASK_SIZE; ++i) {
        ready_coroutine = coroutines[i];

        if (NULL == ready_coroutine) continue;
        if (COROUTINE_READY != ready_coroutine->state) continue;

        break;
    }

    return ready_coroutine;
}

void sched() {
    coroutine_t * ready_coroutine;

    ready_coroutine = find_ready_minithread();
    if (NULL == ready_coroutine) {
        INFO_PRINT("没有需要调度的任务\n");

        return;
    }

    current_coroutine = ready_coroutine;

    ready_coroutine->state = COROUTINE_RUNNING;

    /**
     * mov %%rsp, 0(%%rcx);
        %%rsp（当前栈指针）的值保存到 %%rcx（新的栈指针）所指向的内存地址中。
        这一步是保存当前栈指针，以便稍后恢复。
        0(%%rcx)表示rcx寄存器指向的地址处。
        这句话等于 mov rsp, es:[rcx]

       mov %%rcx, %%rsp;
        将新的栈指针值 %%rcx 赋值给当前栈指针寄存器 %%rsp。
        切换到新的栈空间，rcx 现在是新的栈顶地址。

       callq %%rax;
        调用 %%rax 所指向的函数，即协程函数 ready_coroutine->fun。
        开始执行协程的代码。

       pop %%rsp;
        恢复之前保存的栈指针。
        从新的栈中弹出原来的栈指针，恢复到协程调用前的栈。

       callq %%rbx;
        调用 %%rbx 所指向的函数，即 coroutine_exit。
        协程结束后进行清理操作。

        输入寄存器：
            "a"(ready_coroutine->fun)：%%rax 寄存器保存了协程函数的地址。
            "c"(ready_coroutine->esp3 - 8)：%%rcx 寄存器保存了新的栈指针地址。这里减去 8 字节，是因为栈是向下增长的，而协程函数调用时需要栈顶留出空间存储返回地址等信息。
            "b"(coroutine_exit)：%%rbx 寄存器保存了 coroutine_exit 函数的地址。
        clobbered寄存器：
            "memory"：通知编译器，这段代码会修改内存，因此编译器不会对这段内存的操作进行优化。
     */
    __asm__("mov %%rsp, 0(%%rcx);"  // 保存栈
            "mov %%rcx, %%rsp;"         // 切栈
            "callq %%rax;"              // 协程的执行流
            "pop %%rsp;"                // 恢复rsp
            "callq %%rbx;"              // coroutine_exit()
            ""::"a"(ready_coroutine->fun), "c"(ready_coroutine->esp3 - 8), "b"(coroutine_exit):"memory"
            );
}